﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Flower.Services.Data;
using Jurassic;
using Jurassic.Library;
using Flower.Directory.Scripting.DirectoryEntries;
using Flower.Directory.Scripting.Functions.Processes;
using Flower.Directory.Scripting.Functions.Waiting;
using Flower.Directory.Scripting.Functions.Messaging;
using Flower.Directory.Scripting.Functions.Locking;
using Flower.Directory.Scripting.Functions.Misc;
using Flower.Directory.Scripting.Functions.ServiceContainers;

namespace Flower.Directory.Scripting
{
    public static class Setup
    {
        public sealed class Options
        {
            public Func<ScriptEngine, ObjectInstance> DirectoryObject { get; set; }
            public Func<ScriptEngine, ObjectInstance> FileSystemObject { get; set; }
            public Func<ScriptEngine, ObjectInstance> InObject { get; set; }
            public bool CreateOutObject { get; set; }
        }

        private const string SCRIPTS_NAMESPACE = "Flower.Directory.Scripting.Scripts";

        private static readonly string Scripts;
        
        static Setup()
        {
            System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
            string[] resourceNames = assembly.GetManifestResourceNames();

            var scripts = new StringBuilder();

            foreach (string resourceName in resourceNames)
            {
                if (resourceName.StartsWith(SCRIPTS_NAMESPACE))
                {
                    var stream = assembly.GetManifestResourceStream(resourceName);

                    if (stream != null)
                    {
                        using (var reader = new StreamReader(stream, Encoding.UTF8))
                        {
                            scripts.Append(reader.ReadToEnd());
                        }
                    }
                }
            }

            string scriptsStr = scripts.ToString();

            var removalRegexps = new Dictionary<Regex, string>
            {
                { new Regex(@"/\*(?>(?:(?>[^*]+)|\*(?!/))*)\*/"), string.Empty },
                { new Regex(@"\s{2,}"), " " }
            };

            foreach (var kv in removalRegexps)
            {
                scriptsStr = kv.Key.Replace(scriptsStr, kv.Value);
            }

            Scripts = scriptsStr;
        }

        public static ScriptEngine CreateEngine()
        {
            var engine = new ScriptEngine();
            InitEngine(engine);
            return engine;
        }

        public static ScriptEngine InitEngine(ScriptEngine engine)
        {
            engine.SetGlobalValue("xml", new XmlObjectConstructor(engine));
            engine.SetGlobalValue("boolToXml", new BoolToXmlFunction(engine));
            engine.SetGlobalValue("intToXml", new IntToXmlFunction(engine));
            engine.SetGlobalValue("doubleToXml", new DoubleToXmlFunction(engine));
            engine.SetGlobalValue("stringToXml", new StringToXmlFunction(engine));

            engine.SetGlobalValue("__demandLock", new DemandLock(engine));

            // Processes
            engine.SetGlobalValue("__startProcess", new StartProcess(engine));
            engine.SetGlobalValue("__selectProcessor", new SelectProcessor(engine));
            engine.SetGlobalValue("__saveState", new SaveState(engine));
            engine.SetGlobalValue("__waitProcess", new WaitProcess(engine));
            engine.SetGlobalValue("__changeProcessStatus", new ChangeProcessStatus(engine));
            engine.SetGlobalValue("__changeProcessesStatuses", new ChangeProcessStatuses(engine));
            engine.SetGlobalValue("__wait", new Wait(engine));
            engine.SetGlobalValue("__releaseWaiters", new ReleaseWaiters(engine));
            engine.SetGlobalValue("__suspend", new Suspend(engine));
            engine.SetGlobalValue("__getSuspended", new GetSuspended(engine));
            engine.SetGlobalValue("__enqueueMessage", new EnqueueMessage(engine));
            engine.SetGlobalValue("__dequeueMessage", new DequeueMessage(engine));
            engine.SetGlobalValue("__getMessages", new GetMessages(engine));
            engine.SetGlobalValue("__enqueueMultipleMessages", new EnqueueMultipleMessages(engine));
            engine.SetGlobalValue("__lock", new Lock(engine));
            engine.SetGlobalValue("__unlock", new Unlock(engine));
            engine.SetGlobalValue("__removeAll", new RemoveAll(engine));
            engine.SetGlobalValue("__removeMessage", new RemoveMessage(engine));
            engine.SetGlobalValue("__getResolveLinks", new GetResolveLinks(engine));
            engine.SetGlobalValue("__getServiceContainer", new GetServiceContainer(engine));
            engine.SetGlobalValue("__getExpiredServiceContainersIds", new GetExpiredServiceContainersIds(engine));

            engine.SetGlobalValue("Assembly", new AssemblyObjectConstructor(engine));
            engine.SetGlobalValue("Folder", new FolderObjectConstructor(engine));
            engine.SetGlobalValue("Link", new LinkObjectConstructor(engine));
            engine.SetGlobalValue("Message", new MessageObjectConstructor(engine));
            engine.SetGlobalValue("Process", new ProcessObjectConstructor(engine));
            engine.SetGlobalValue("Processor", new ProcessorObjectConstructor(engine));
            engine.SetGlobalValue("ServiceContainer", new ServiceContainerObjectConstructor(engine));
            engine.SetGlobalValue("Set", new SetObjectConstructor(engine));
            engine.SetGlobalValue("State", new StateObjectConstructor(engine));
            engine.SetGlobalValue("Workflow", new WorkflowObjectConstructor(engine));
            engine.SetGlobalValue("Role", new RoleObjectConstructor(engine));

            engine.SetGlobalValue("DET_ALL", (int)DirectoryEntryTypes.All);
            engine.SetGlobalValue("DET_ASSEMBLY", (int)DirectoryEntryTypes.Assembly);
            engine.SetGlobalValue("DET_FOLDER", (int)DirectoryEntryTypes.Folder);
            engine.SetGlobalValue("DET_LINK", (int)DirectoryEntryTypes.Link);
            engine.SetGlobalValue("DET_MESSAGE", (int)DirectoryEntryTypes.Message);
            engine.SetGlobalValue("DET_PROCESS", (int)DirectoryEntryTypes.Process);
            engine.SetGlobalValue("DET_PROCESSOR", (int)DirectoryEntryTypes.Processor);
            engine.SetGlobalValue("DET_ROLE", (int)DirectoryEntryTypes.Role);
            engine.SetGlobalValue("DET_SERVICE_CONTAINER", (int)DirectoryEntryTypes.ServiceContainer);
            engine.SetGlobalValue("DET_SET", (int)DirectoryEntryTypes.Set);
            engine.SetGlobalValue("DET_STATE", (int)DirectoryEntryTypes.State);
            engine.SetGlobalValue("DET_WORKFLOW", (int)DirectoryEntryTypes.Workflow);

            engine.SetGlobalValue("SORT_DEFAULT", (int)SortProperty.Default);
            engine.SetGlobalValue("SORT_BY_NAME", (int)SortProperty.Name);
            engine.SetGlobalValue("SORT_BY_UPDATE_TIMESTAMP", (int)SortProperty.UpdatedOn);
            engine.SetGlobalValue("SORT_BY_CREATION_TIMESTAMP", (int)SortProperty.CreatedOn);

            engine.SetGlobalValue("ASC", (int)SortOrder.Asc);
            engine.SetGlobalValue("DESC", (int)SortOrder.Desc);

            engine.SetGlobalValue("BLOB_FORMAT_TEXT", (int)BlobFormat.TextXml);
            engine.SetGlobalValue("BLOB_FORMAT_BIN", (int)BlobFormat.BinXml);
            engine.SetGlobalValue("BLOB_FORMAT_GZIP", (int)BlobFormat.GZipXml);

            engine.SetGlobalValue("READ", (int)Permission.Read);
            engine.SetGlobalValue("WRITE", (int)Permission.Write);
            engine.SetGlobalValue("CREATE_CHILDREN", (int)Permission.CreateChildren);

            engine.SetGlobalValue("$xmlns", engine.Object.Construct());

            engine.Execute(Scripts);

            return engine;
        }
    }
}
